home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 09 - 1993 / 09.07 Jul 93 / Programmers' Challenge / MagicAdd.c
Encoding:
Text File  |  1993-05-11  |  11.6 KB  |  392 lines  |  [TEXT/KAHL]

  1. /**********************************************************
  2.  * MagicAdd.c
  3.  *
  4.  * Purpose:
  5.  *    This file contains the function CountMagicAdditions()
  6.  *    which solves the May Programmer's Challenge from
  7.  *    MacTech Magazine.
  8.  *    
  9.  *    In the functions, rather than using abc + def = ghj,
  10.  *    the variables are repsented by the following notation:
  11.  *           x  x  x
  12.  *            2  1  0
  13.  *        +  y  y  y
  14.  *            2  1  0
  15.  *       -----------  , i.e. x2 = a, x1 = b, y2 = d, etc.
  16.  *           z  z  z
  17.  *            2  1  0
  18.  *    
  19.  *    CountmMagicAdditions() and its supporting function
  20.  *    NFind() calculates the number of equations which satisfy
  21.  *    the problem abc + def = ghj, where a...j represent a
  22.  *    digit from 1...9 and each digit appears only once in
  23.  *    the equation.
  24.  *    
  25.  *    The main optimization is a property of the problem 
  26.  *    which is, g+h+j = 18. My friend, Paul Fieguth,  
  27.  *    developed a proof for this which is given later on.
  28.  *    
  29.  *    Several other properties are used in order to reduce 
  30.  *    the run time such as:
  31.  *        
  32.  *    Only one carry is ever generated - another property
  33.  *    from Paul Fieguth's proof.
  34.  *    
  35.  *    Basic limits:
  36.  *    3 <= a+b <= 17 where 1 <= a,b <= 9 and no digit is
  37.  *    used twice
  38.  *    
  39.  *    If no carry is generated in the addition then
  40.  *    3 <= a+b <= 9 where 1 <= a,b < 9 and no digit is used
  41.  *    twice
  42.  *    
  43.  *    If a carry is generated in the addition then
  44.  *    11 <= a+b <= 17 where 1 < a,b <= 9 and no digit is
  45.  *    used twice
  46.  *    
  47.  *    Never divide by 2 when you can shift left by 1.
  48.  *    
  49.  *    The function starts by determining valid 'ghj'.
  50.  *    Since g+h+j = 18, it can easily reduce the possible # 
  51.  *    of valid sums by only generating g,h,j such that their 
  52.  *    sum equals 18.
  53.  *    It starts by iterating through valid values of g which
  54.  *    are from 4 to 9. 3 is excluded since the possible 
  55.  *    values of ghj where g=3 are invalid [See proof later
  56.  *    on].
  57.  *    After selecting a g, h & j are selected. The
  58.  *    expression (9-z2) is used to start the selection of
  59.  *    the values of h & j since the max value of either h or
  60.  *    j is 9. Say h == 9, that means g + j = 18 - (h=9) = 9.
  61.  *    So j = 9 - g, which translates into (9-z2) in the z0
  62.  *    for-loop.
  63.  *    
  64.  *    A check is made to ensure that no digit appears more 
  65.  *    than once in ghj and that g,h,j are within the range
  66.  *    1...9.
  67.  *    
  68.  *    The fBitUsed bit-array is updated to reflect
  69.  *    those digits that are being used.
  70.  *    
  71.  *    Next, the function selects valid a & d values.
  72.  *    This is simplified once we have a valid ghj since
  73.  *    the values of a & d are restricted by the currently-
  74.  *    selected g. It iterates from the (g-1)/2 to 1 for
  75.  *    values of a. This helps generate a,d,g such that
  76.  *    a<d<g. This method is used frequently.
  77.  *    
  78.  *    A check is made to ensure that a isn't being used
  79.  *    in ghj. The function then calculates what d should
  80.  *    be in order to satisfy a + d = g.
  81.  *    
  82.  *    If d isn't being used in ghj, and j < 8
  83.  *    (a property of the fact that the tens column didn't 
  84.  *    generate a carry, so the ones column must generate
  85.  *    a carry. If the ones column does generate a carry. then
  86.  *    the valid values for j are limited) a call is made
  87.  *    to NFind to determine if the currently selected
  88.  *    digits form the basis of an equation which satisfies
  89.  *    the problem. If it does, then the # of valid solutions
  90.  *    is incremented.
  91.  *    
  92.  *    d is then decremented to investigate the case where
  93.  *    a + d + 1 = g, i.e. a carry is generated from the
  94.  *    tens column, (b+e = h, h>10). There are checks
  95.  *    made to ensure that the new d isn't being used in ghj
  96.  *    and that a<d and j > 2 (once again this is a property
  97.  *    of the fact that the tens column did generate a carry
  98.  *    so the ones column didn't generate a carry and the
  99.  *    values of j are therefore restricted once again).
  100.  *    If the checks succeed, a call to NFind is made to
  101.  *    see if the current values of a, d, ghj form the
  102.  *    basis of an equation which satisfies the problem.
  103.  *    
  104.  *    For NFind, there are two major cases to handle.
  105.  *    One where the tens column generates a carry and one
  106.  *    where the tens column doesn't generate a carry (which
  107.  *    means that the ones column DOES generate a carry - 
  108.  *    the hundreds column can't generate a carry).
  109.  *    The first if-statement in NFind handles initializing
  110.  *    the for-loop start & end parameters. The start and
  111.  *    end parameters are derived from the tables for valid
  112.  *    a + b = c shown later on.
  113.  *    
  114.  *    In the for-loops, valid c & f are selected and
  115.  *    checked to see if the digits they correspond to are
  116.  *    already being used. If not, then the second for-loop
  117.  *    is entered to determine valid b & e. Once again
  118.  *    checks to ensure that the digits corresponding to b & e
  119.  *    aren't being used are made. If they aren't then we have
  120.  *    a solution!
  121.  *    
  122.  * Owner:
  123.  *    Johnny Lee
  124.  *********************************************************/
  125.  
  126. /**********************************************************
  127.  *    Proofs & Tables
  128.  *    
  129.  *    [Paul Fieguth's proof]
  130.  *    
  131.  *    The problem is:
  132.  *    Find all of the correct addition problems of the form:
  133.  *    123 + 456 = 789 using each of 1 through 9 once each in
  134.  *    every solution.
  135.  *    
  136.  *    Now, in performing the addition, if a carry is never
  137.  *    generated ...
  138.  *         a+b+c+d+e+f = g+h+j
  139.  *    
  140.  *    If one carry is generated, then a value of 10 is
  141.  *    turned into a 1 in the next higher column, e.g.,
  142.  *         a+b+c+d+e+f = g+h+j + 9    (9 = 10 - 1)
  143.  *    
  144.  *    In general, we can say
  145.  *         a+b+c+d+e+f = g+h+j + n*9  n = 0,1,2,3,...
  146.  *    
  147.  *    Now, the sum of digits from one to 9 is
  148.  *     1 + ... + 9 = 45
  149.  *         Let    x = g+h+j     x integer
  150.  *         i.e.,  x + (x + n*9) = 45
  151.  *         Consider each value of  n  separately:
  152.  *           n=0   x = 45 - x      ->  x = 45/2 not integer
  153.  *           n=1   x = 45 - 9 - x  ->  x = 36/2 = 18
  154.  *           n=2   x = 45 -18 - x  ->  x = 27/2 not integer
  155.  *           n=3   x = 45 -27 - x  ->  x = 18/2 = 9
  156.  *    
  157.  *    But n=3 is not possible, since we are adding 3 sets of
  158.  *    digits, and the last (hundreds colunm) cannot generate
  159.  *    a carry.  ie, n <= 2
  160.  *    
  161.  *    But by the previous argument, if n<=2, then n=1, and
  162.  *    thus x=18.
  163.  *    
  164.  *    Therefore  g+h+j = 18 (1) for all satisfying solutions.
  165.  *    Also, only one carry is ever generated.
  166.  *    
  167.  *    [Valid values for g]
  168.  *    
  169.  *    If we look at the values that g can take:
  170.  *        g    18-g    h,j
  171.  *        9    9        [1,8],[2,7],[3,6],[4,5],[5,4],[6,3],
  172.  *                        [7,2],[8,1]
  173.  *        8    10        [1,9],[3,7],[4,6],[6,4],[7,3],[9,1]
  174.  *        7    11        [2,9],[3,8],[5,6],[6,5],[8,3],[9,2]
  175.  *        6    12        [3,9],[4,8],[5,7],[7,5],[8,4],[9,3]
  176.  *        5    13        [4,9],[6,7],[7,6],[9,4]
  177.  *        4    14        [5,9],[6,8],[8,6],[9,5]
  178.  *    
  179.  *    g can't take take a value less than 3 because the
  180.  *    minimum sum of two single-digit integers is 3, i.e
  181.  *    1+2 - remember we're considering the hundreds column
  182.  *    so g can't be greater than 9.
  183.  *    
  184.  *    Now 3 is also invalid because the set of numbers which
  185.  *    satisfy (1) for [h,g] are [6,9], [7,8], [8,7], [9,6].
  186.  *    Now the proof above states that there will be one
  187.  *    carry generated for the given equation.
  188.  *    
  189.  *    Now consider each set of [h,j] for g = 3:
  190.  *    h=6, j=9:
  191.  *    if j=9, it can't generate the carry since the 
  192.  *    max result of adding two single digit number is
  193.  *    17 (=8+9) (2).
  194.  *    
  195.  *    So h=6 must generate the carry. But the only two sums
  196.  *    that could generate a 16, (7+9, 8+8) are invalid since
  197.  *    the former contains a digit which is being used in j
  198.  *    and the latter contains the same two digits.
  199.  *    
  200.  *    h=7, j=8:
  201.  *    if j=8, it can't generate the carry due to (2),
  202.  *    Therefore, h=7 must generate the carry. But the only
  203.  *    addition which can generate 17 is 8+9 and 8 is being
  204.  *    used already in j.
  205.  *    
  206.  *    h=8, j=7:
  207.  *    if j=7, it can't generate the carry since the two
  208.  *    numbers needed to generate 17 are 8,9, but 8 is being
  209.  *    used in h. Therefore h=8 must generate the carry, but
  210.  *    it can't according to (2).
  211.  *    
  212.  *    h=9, j=6:
  213.  *    if j=6, it can't generate the carry since the only
  214.  *    valid addition for this problem which can generate
  215.  *    16 uses 7 & 9, but h has the value 9.
  216.  *    Therefore h=9 must generate the carry, but it can't
  217.  *    according to (2).
  218.  *            
  219.  *    [How to ensure a<d<g for a + d = g]
  220.  *    
  221.  *    Now since abc + def = def + abc = ghj, where a<d<g, the
  222.  *    problem specifies that only one of abc+def & def+abc
  223.  *    can be counted as a solution.
  224.  *    Therefore, when determining a & d, a only needs to
  225.  *    iterate from 1 to (g-1)/2 because if a >= g/2 then
  226.  *    d <= a otherwise we'd have an overflow of the hundreds
  227.  *    column and the problem doesn't allow that.
  228.  *    We check for solutions with a = 1 ... (g-1)/2, and
  229.  *    all solutions for a<g/2 will have been counted already,
  230.  *    so if d is less than a, the solution will be counted
  231.  *    twice.
  232.  *    
  233.  *    [Valid a,b,c for a + b = c]
  234.  *    If a carry is generated by a + b = c, then for 
  235.  *    valid c (10<c<18):
  236.  *    c    [a,b] pairs
  237.  *    11    [2,9], [3,8], [4,7], [5,6]
  238.  *    12    [3,9], [4,8], [5,7]
  239.  *    13    [4,9], [5,8], [6,7]
  240.  *    14    [5,9], [6,8]
  241.  *    15    [6,9], [7,8]
  242.  *    16    [7,9]
  243.  *    17    [8,9]
  244.  *    
  245.  *    Therefore:
  246.  *    a = (c mod 10)+1...((c mod 10)+9)/2
  247.  *    b = c - a
  248.  *    
  249.  *    If no carry is generated by a + b = c, then for
  250.  *    valid c (2<c<10):
  251.  *    c    [a,b] pairs
  252.  *    3    [1,2]
  253.  *    4    [1,3]
  254.  *    5    [1,4], [2,3]
  255.  *    6    [1,5], [2,4]
  256.  *    7    [1,6], [2,5], [3,4]
  257.  *    8    [1,7], [2,6], [3,5]
  258.  *    9    [1,8], [2,7], [3,6], [4,5]
  259.  *    
  260.  *    a = 1...(c-1)/2
  261.  *    b = c - a
  262.  *********************************************************/
  263.  
  264. /* Raise 2 to the power of x */
  265. #define PowerTwo(x) (1<<(x))
  266.  
  267. /* Determine if digit/bit Y in the word X is set */
  268. #define FIsDigitUsed(x,y) ((x) & PowerTwo(y))
  269.  
  270. /*    NFind
  271.  *    
  272.  *    Purpose:
  273.  *        Finds out if there are any equations which fit the
  274.  *        equation, abc + def = ghj given the parameters
  275.  *        passed in.
  276.  *    
  277.  *    Arguments:
  278.  *        fDigitsUsed        buffer where every bit that's set 
  279.  *                        represents a digit that's been used.
  280.  *        z0                the one digit of the sum
  281.  *        z1                the tens digit of the sum
  282.  *        fCarryFromTensColumn    if the tens column
  283.  *                                generates a carry.
  284.  *    
  285.  *    Returns:
  286.  *        Number of equations which satisfy the problem.
  287.  *        Can be either 4 or 0.
  288.  */
  289. short
  290. NFind(short fDigitsUsed, short z0, short z1,
  291.     short fCarryFromTensColumn)
  292. {
  293.     register short x0;
  294.     register short x1;
  295.     register short y0;
  296.     register short fDigitsUsedT;
  297.     short y1;
  298.     short nStop;
  299.     short nStartX1;
  300.     short nStopX1 = 10;
  301.     
  302.     if ( fCarryFromTensColumn ) {
  303.         x0 = (z0-1)>>1;
  304.         nStartX1 = z1+1;
  305.         z1 += 10;
  306.         nStop = 0;
  307.     } 
  308.     else {
  309.         x0 = (z0+9)>>1;
  310.         --z1;
  311.         nStop = z0;
  312.         z0 += 10;
  313.         nStartX1 = 1;
  314.     }
  315.  
  316.     for (; x0>nStop; --x0) {
  317.         if (FIsDigitUsed(fDigitsUsed,x0))
  318.             continue;
  319.         y0 = z0 - x0;
  320.         if (FIsDigitUsed(fDigitsUsed, y0))
  321.             continue;
  322.         fDigitsUsedT = fDigitsUsed | PowerTwo(x0) | PowerTwo(y0);
  323.         for (x1 = nStartX1; x1<nStopX1; ++x1) {
  324.             if (FIsDigitUsed(fDigitsUsedT,x1))
  325.                 continue;
  326.             y1 = z1 - x1;
  327.             if (FIsDigitUsed(fDigitsUsedT,y1))
  328.                 continue;
  329.             if (y1 == x1)
  330.                 continue;
  331.             return 4;        
  332.         }    
  333.     }
  334.     return 0;
  335. }
  336.  
  337. /*    CountMagicAdditions
  338.  *    
  339.  *    Purpose:
  340.  *        Calculates the number of equations which satisfy
  341.  *        the problem abc + def = ghj where a-j represent
  342.  *        a digit 1...9 and each digit appears only once
  343.  *        in the equation.
  344.  *    
  345.  *    Returns:
  346.  *        Unsigned short    number of Magic Additions
  347.  */
  348. unsigned short
  349. CountMagicAdditions()
  350. {
  351.     short z0;
  352.     short z1;
  353.     short z2;
  354.     short y2;
  355.     short x2;
  356.     short fDigitsUsed;
  357.     short w;
  358.     short cSolutions = 0;
  359.  
  360.     for (z2 = 9, w = 18 - z2; z2 > 3; --z2, ++w) {
  361.         for (z0 = (z2<9) ? 9 - z2 : 1; z0<10; ++z0) {
  362.             z1 = w - z0;
  363.             if ((z2 == z0) || (z2 == z1) ||
  364.               (z0 == z1) || (z1<1))
  365.                 continue;
  366.             fDigitsUsed = PowerTwo(z2) | PowerTwo(z1) |
  367.               PowerTwo(z0);
  368.             
  369.             for (x2 = (z2-1)>>1; x2 > 0; --x2) {
  370.                 if (FIsDigitUsed(fDigitsUsed,x2))
  371.                     continue;
  372.                 y2 = z2 - x2;
  373.  
  374.                 if (!FIsDigitUsed(fDigitsUsed,y2) &&
  375.                   (z0<8)) {
  376.                     cSolutions += NFind(fDigitsUsed |
  377.                       PowerTwo(x2) | PowerTwo(y2), z0,
  378.                       z1, 0);
  379.                 }
  380.                 --y2;
  381.                 if (!FIsDigitUsed(fDigitsUsed,y2) &&
  382.                   (y2 > x2) && (z0 > 2)) {
  383.                     cSolutions += NFind(fDigitsUsed |
  384.                       PowerTwo(x2) | PowerTwo(y2), z0,
  385.                       z1, 1);
  386.                 }
  387.             }
  388.         }
  389.     }
  390.     return cSolutions;
  391. }
  392.